/** Author: JTrapp@Google.com
* (c) 07.01.2008 */

/**
* Access to the map
* @type GMap2
*/
var map;
/**
* Access to the Geocoder
* @type GClientGeocoder
*/
var geocoder;
var adminMode;
var myIcon;
var tableDiv;
var indices = new Object();

//Configuration ahead
var DELIMITER="\t";
//Retrieve all indices from the table
var latitudeLabel="LATITUDE";
var longitudeLabel="LONGITUDE";
var accuracyLabel="ACCURACY";
var addressFormat = "{ADDRESS_LINE_1} {ADDRESS_LINE_2}, {POSTAL_CODE},{COUNTRY_CODE}"; //addr1+ " " + addr2+","+Postal+","+Country

var infoFormat = "<h1 class=\"popup\"><a href=\"{HOME_PAGE}\">{NAME}</a></div>"+
                    "<p class=\"popup\">{DESCRIPTION}</p>"+
                    "<table><tr><td>{ADDRESS_LINE_1}</td><td rowspan=\"4\">"+/*put image here*/"</td></tr>"+
                    "<tr><td>{ADDRESS_LINE_2}</td></tr>"+
                    "<tr><td>{CITY},{POSTAL_CODE}</td></tr>"+
                    "<tr><td>Phone: {MAIN_PHONE}</td></tr>"+
                    "<tr><td>Fax: {FAX_PHONE}</td></tr>"+
                    "</table>";
                    

var gDataLabels ="gsx:name,gsx:description,gsx:homepage,gsx:addressline1,gsx:addressline2,gsx:city"
        +"gsx:postalcode,gsx:countrycode,gsx:mainphone,gsx:faxphone,gsx:latitude,gsx:longitude".split(",");



/** Create the icon*/
function createIcon(){
    var iconURL = prefs.getMsg("iconURL");
    if (iconURL!=null && iconURL.length>0){
        prefs.getMsg("iconURL");
        myIcon = new google.maps.Icon();
        myIcon.iconSize = new google.maps.Size(prefs.getMsg("iconWidth"), prefs.getMsg("iconHeight"));
        myIcon.iconAnchor = new google.maps.Point(prefs.getMsg("iconAnchorX"), prefs.getMsg("iconAnchorY"));
        myIcon.infoWindowAnchor = new google.maps.Point(prefs.getMsg("iconWindowX"),prefs.getMsg("iconWindowY"));
        myIcon.infoWindowAnchor = myIcon.iconAnchor;
        myIcon.image = _IG_GetImageUrl(iconURL);
    }
}



function SF_init() {
    //_gel("SF_div").innerHTML="<div id=\"mapDiv\" class=\"map\">"+prefs.getMsg("loadingMap")+"</div><br/><div id=\"status\" class=\"status\"><div>";
    map = new google.maps.Map2(_gel("SF_map"));
    map.setCenter(new google.maps.LatLng(41.875696,-87.624207), 3); 
    geocoder = new google.maps.ClientGeocoder();        
    createIcon();
    tableDiv = _gel("table");  
    var requestURL = prefs.getString("storeListURL");
    adminMode = prefs.getBool("admin");
    if (adminMode){
        google.maps.Log.writeUrl(requestURL);      
    }
    var len = requestURL.length;
    var spreadsheetsBaseURL = "http://spreadsheets.google.com/";
    var suffix = requestURL.substring(len-4, len);
    
    if ((suffix == ".csv")||(suffix == ".txt")){
        readCSV(requestURL);
    }else if (requestURL.substring(0,spreadsheetsBaseURL.length) == spreadsheetsBaseURL){
        readSpreadsheetGData(requestURL);
    }
    initMapControls();
}

// Read fcsv data in csv form.
function readCSV(requestURL){
    //google.maps.Log.write( "<a href=\""+requestURL+"\">Fetching storelist.</a>");
    _IG_FetchContent(requestURL, function (responseText) {
        var lines = responseText.split("\n");
        var startIdx=0;
        while (startIdx<lines.length&&lines[startIdx].substring(0,"STORE_CODE".length)!="STORE_CODE"){
            startIdx++;
        }
        if (startIdx<lines.length){
            var labels = lines[startIdx++].split(DELIMITER);
            for (var i=0; i< labels.length; i++){
                labels[i]= labels[i].replace(/^\s+|\s+$/,""); //trim...
            }

            if (getIndex(latitudeLabel)==-1){ // Add latitude, longitude for output table
                labels[labels.length]=latitudeLabel;
                labels[labels.length]=longitudeLabel;
            }
            if (adminMode){
                printHeader(labels);
            }
            var numShops = lines.length-startIdx;
            if (adminMode){
                google.maps.Log.writeHtml( "<a href=\""+requestURL+"\">"+numShops+ " Shops loaded.</a>");      
            }
            var bounds = new google.maps.LatLngBounds( );    
            for (var i = startIdx; i<numShops; i++) {
                var c = lines[i].split(DELIMITER);
                createMarkerCSV(labels, c, bounds, i, printMarker);
            }    
            //google.maps.Log.writeHtml( "<a href=\""+requestURL+"\">"+numShops+ " addresses loaded.</a>");     
        } else if (adminMode) {
            google.maps.Log.writeHtml( "<a href=\""+requestURL+"\">Illegal format in shoplist.</a>");
            google.maps.Log.write(response);
        }
    });
}


function readSpreadsheetGData(requestURL){
    _IG_FetchXmlContent(requestURL, function (response) {  
        //google.maps.Log.write( "Spreadsheet"+response.getElementsByTagName("title").item(0).nodeValue);
        _IG_SetTitle(response.getElementsByTagName("title").item(0).nodeValue);
        var bounds = new google.maps.LatLngBounds( );    
        var entries = response.getElementsByTagName("entry");
        if (entries!=null){
            if (adminMode){
                google.maps.Log.write( "Geocoding"+entries.length+" addresses.");
            }
            for (var i = 0; i < entries.length ; i++) { 
                // For each <food> node, get child nodes.
                createMarkerSpreadsheetGData(entries.item(i), bounds, i, printMarker);
            }
            map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds)); 
        } else if (adminMode){
            google.maps.Log.writeHtml( "<a href=\""+requestURL+"\">Illegal format in shoplist.</a>");
            google.maps.Log.write(response);
        }
    },10 );
}

function initMapControls() {
    map.addControl(new google.maps.SmallMapControl());
    map.setMapType(G_HYBRID_MAP);
    if (prefs.getBool("showMapTypeControls")) { 
        map.addControl(new google.maps.MapTypeControl());
    }
    //
    //    if (prefs.getBool("showSearchControls")){
    //        // create a local search control and add it to your map
    //        google.load("uds", "1");
    //
    //        var lsc = new google.maps.LocalSearch(); 
    //        map.addControl(new google.maps.LocalSearch());
    //    }
    
}


/**
* Create a marker on the map for the given address. 
*/
function createMarkerCSV(labels, values, bounds, rowNr, callback) {
    var longitude = values[getIndex(longitudeLabel)];
    if ((longitude == undefined)||(longitude == "undefined")||(longitude == "")) {  // With Geocoding request
        createMarkerGeocode(labels, values, bounds, rowNr, callback, 0);
    } else { //without geocoding request
        createMarker(labels, values, bounds, rowNr, callback);
    }
}

/**
* Select fields from the entry to create the marker.
*/
function createMarkerSpreadsheetGData(entry, bounds, rowNr, callback) {
    var values = new Array();
    for (var i=0; i<gDataLabels.length; i++){
        values[i] = getNodeValue(entry, gDataLabels[i]);
    }
    createMarker(gDataLabels,values, bounds, rowNr, callback);
}


function formatMessage(format, labels, values){
    var message = format; // Create new Ob ject
    for (var i=0; i<labels.length;i++){
        var regex1 = new RegExp("[{]"+labels[i]+"[}]","gi");
        message = message.replace(regex1, values[i]);
        var regex2 = new RegExp("[{]"+i+"[}]","g");
        message = message.replace(regex2, values[i]);
    }
    return message;
}


function createMarkerGeocode(labels, values, bounds, rowNr, callback, count) { 
    var address = formatMessage(addressFormat, labels, values);

    geocoder.getLocations(address, function(response) {
        //google.maps.Log.write( "Geocoding "+address+" address on line "+rowNr);
       if (!response ) {
            google.maps.Log.write("Failed geocoding request for line "+rowNr);
            callback(rowNr, values);
       } else if (response.Status.code == 620) { // too many request, retry later
            if (count<5){
                window.setTimeout( function(){
                        createMarkerGeocode(labels, values, bounds, rowNr, callback, count+1);
                    }, 500*rowNr*(count+1));
            } else {
                google.maps.Log.write("Failed geocoding "+address +" with status "+response.Status.code+" on line "+rowNr+" after "+count+" retries.");
                values[getIndex(labels, latitudeLabel)]="undefined"; //mark latitude
                values[getIndex(labels, longitudeLabel)]="undefined"; //mark longitude
                values[getIndex(labels, accuracyLabel)]="undefined"; //mark longitude
                callback(rowNr, values);
            }
        } else if (response.Status.code != 200) {
                google.maps.Log.write("Failed geocoding "+address +" with status "+response.Status.code+" on line "+rowNr+".");
                values[getIndex(labels, latitudeLabel)]="undefined"; //mark latitude
                values[getIndex(labels, longitudeLabel)]="undefined"; //mark longitude
                values[getIndex(labels, accuracyLabel)]="undefined"; //mark longitude
                callback(rowNr, values);
        } else { // success
            values[getIndex(labels, latitudeLabel)] = response.Placemark[0].Point.coordinates[1];
            values[getIndex(labels, longitudeLabel)] = response.Placemark[0].Point.coordinates[0];
            values[getIndex(labels, accuracyLabel)] = response.Placemark[0].AddressDetails.Accuracy;
            createMarker(labels, values, bounds, rowNr, callback);   
            if (adminMode){
                google.maps.Log.write( "Geocoded "+address+" address  on line "+rowNr+" ("+response.Placemark[0].Point.coordinates[1]+","+response.Placemark[0].Point.coordinates[0]+") Accuracy: "+response.Placemark[0].AddressDetails.Accuracy+")");
            }
        }
    });
}


function createMarker(labels, values, bounds, rowNr, callback) {
        var point = new google.maps.LatLng(
            values[getIndex(labels, latitudeLabel)], values[getIndex(labels, longitudeLabel)]);
        var marker = new google.maps.Marker(point, {icon:myIcon});
        GEvent.addListener(marker, "click", function() {
            marker.openInfoWindowHtml(formatMessage(infoFormat, labels, values),{maxWidth : 120});
        });
        map.addOverlay(marker);
        callback(rowNr, values);
        bounds.extend(point);
        map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
        //google.maps.Log.write("Bounds: ("+bounds.getSouthWest().lat()+","+bounds.getSouthWest().lng()+") ("+bounds.getNorthEast().lat()+""+bounds.getNorthEast().lng()+") "+map.getBoundsZoomLevel(bounds));           
    }
    
    
    /** Print a line of values. Only called in admin mode.*/
    function printMarker(rowNr, values){
        tableDiv.innerHTML += values[0];
        for (var i=1; i<values.length; i++){
            tableDiv.innerHTML += DELIMITER+values[i];
        }
        tableDiv.innerHTML += "\n";
    }
    
   
   /** Print the table header. Only called in admin mode.*/
   function printHeader(labels){
        tableDiv.innerHTML = labels[0];
        for (var i=1; i<labels.length; i++){
            tableDiv.innerHTML += DELIMITER+labels[i];
        }
        tableDiv.innerHTML += "\n";
    }
    
/**
* Retrieve the first child element for a given tag.
* this method will only look for direct childs. No grandchilds will be found.
* For some an unknown reason, I didn't get the standard getElementsByTagNameNS 
* to work. If you know how, please substitute and let me know.

* @param {Element} element Some Dom element
* @param {String} tagname The child elements tag name, which may include the namespace prefix.
* @return {Node}Child element or the String "[<"+tagname+"> null]" or "[<"+tagname+"> not found]". 
*/
function getNodeValue(element, tagname) {
    var nodeList = element.childNodes;
    for (var i = 0, node; node = nodeList[i]; i++) {
        if (node.nodeName == tagname) {
            if (node.firstChild!=null){
                return node.firstChild.nodeValue;
            } else {
                return "";
            }
        } else {
            var result = getNodeValue(node, tagname);
            if (result!=null) {
                return result;
            }
        }
    }
    return null;
}



/** 
* Get the index for a certain keyword in an array of strings.
* Index will be cached, if the value has been found. If the value
* has not been found, '-1' is returned, wich is not stored. 
*/
function getIndex(labels, key) {
    var index = indices[key];
    if (index!=null){
        return index;
    }
    for (var i =0, string; string = labels[i]; i++) {
        if (string == key) {
            indices[key]=i; // Remember this for the next time.
            return i;
        }
    }
    return -1; //do not store this result in dices, because the key may be added to header later
}


